﻿
using hentity;
using hlog;
using hq;
using hutil;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace hcache {
    /**
    *┌────────────────────────────────────────────────┐
    *│　描    述：TransferConfigFacade                                                    
    *│　作    者：sgj                                              
    *│　版    本：1.0                                              
    *│　创建时间：2020/10/10 10:12:05                        
    *└────────────────────────────────────────────────┘
    **/
    public class TransferConfigFacade {
        private static List<Type> taskList = new List<Type>();

        private static List<String> taskNameList = new List<String>();
        private static Dictionary<String, Type> taskDict = new Dictionary<String, Type>();
        public static Type robotType;

        public static void register<T> () where T : BaseTask {
            Type t = typeof(T);
            foreach (var v in taskList) {
                if (v == t) {
                    return;
                }
            }
            taskList.Add(t);
            String name = t.Name;
            taskNameList.Add(name);
            taskDict.Add(name, t);
        }
        public static List<Type> getAllTaskType () {
            return taskList;
        }

        public static Type getType (String name) {
            if (taskDict.ContainsKey(name)) {
                return taskDict[ name ];
            } else {
                throw new Exception("设备类型没有注册" + name);
            }

        }


        internal static List<Edge> allEdge = new List<Edge>();
        private static object lockObj = new object();
        static void clearTmp() {
            List<Node> allDict = MyCache.getList<Node>();
            foreach( var v in allDict ) {
                v.clearTmp();
            }
        }

        public static  void  remove(Estimate e) {

            Node nextNode = getConfig(e.nodeId);
            nextNode.timeAxis.Remove(e.id);
        }
        

        public static void addQueue(BStnRelation physical) {
            Type relyName = getType(physical.deviceType);
            Node b = getConfig(physical.src);
            Node t = getConfig(physical.next);
            if( b == null || t == null ) {
                throw new Exception("配置路径异常" + physical.id);

            }
            b.addGoalConfig(t ,relyName ,physical);
        }
        /// <summary>
        /// 通过传一个key获取点位
        /// </summary>
        /// <param name="eid"></param>
        /// <returns></returns>
        public static Node getConfig(int eid ,bool setNewEid = false) {

            List<Node> allDict = MyCache.getList<Node>();
            Node res = null;
           
                foreach( var config in allDict ) {
                 
                    if( config.isConfig(eid) ) {
                        res = config;
                        break;
                    }
                }
            

            if( setNewEid ) {
                res.eid = eid;
            }
            return res;
        }

        public static BaseTask getCurrenTask(int eid ,CoreTaskParam param) {
            Node node =getConfig(eid);
            List<long> timeAxis = node.timeAxis;
            BaseTask bt = null;
            foreach( long id in timeAxis ) {
                Estimate e = MyCache.getValue<Estimate>("" + id);
                if( e.taskNo == param. taskNo ) {


                    BaseTask tdto = getTaskById(e.myOrder);
                    if( tdto.src == param.cStn && tdto.des == param.cDes ) {
                        if( bt != null ) {
                            throw new Exception(param.taskNo +"有多个任务"+ eid);
                        }
                        bt = tdto;
                    }

                }


            }

            return bt;
        }

        public static List<TaskDto> getTaskDtoList(CoreTaskParam p) {
            List<TaskDto> keyList = null;
            if( p.status >= ( int ) CoreTaskParamStatus.待执行 && p.status < ( int ) CoreTaskParamStatus.执行结束 ) {
                keyList = getTaskPath(p.cStn ,p.cDes ,p.cTaskName);
                keyList[0].initStatus = (CoreTaskParamStatus) p.status;
                if( p.cDes != p.des ) {
                    keyList.AddRange(getTaskPath(p.cDes ,p.des ,null));
                }
            } else {
                keyList = getTaskPath(p.cStn ,p.des ,null);
            }

            if( keyList == null || keyList.Count == 0 ) {
                throw new Exception(p.src + "" + p.des + "路径失败");
            }

            return keyList;
        }
        /// <summary>
        /// 添加一个范围点位
        /// </summary>
        /// <param name="eid">点位</param>
        /// <param name="begin">范围起点</param>
        /// <param name="end">范围终点</param>
        /// <returns></returns>
        public static Node addTransferRange(BStn stn) {
            int eid = stn.eid;
            Node node = getConfig(eid);
            if(node == null ) {
                node = new Node(stn);
                MyCache.addOver(  node);
            }
            node.addConfig(stn);
            return node;
        }
        /// <summary>
        /// 添加关键点位
        /// </summary>
        /// <param name="stnList"></param>
    


       public static Estimate setTaskDto(BaseTask task ,List<Node> list ,Estimate before) {
           
          
            int conmonBeforeTime = 0;
            if( before != null ) {//中间任务的起始地址
                Node b = getConfig(before.nodeId);
                conmonBeforeTime = getEdgeTime(b ,list[0]);
                before.conmonNextTime = conmonBeforeTime;
            }

            for( int i = 0;i < list.Count ;i++ ) {
                Node node = list[i];
                Estimate estimate = node.getEstimate(task.taskNo);
            
                
                if( estimate == null ) {
                    conmonBeforeTime = before != null ? before.conmonNextTime : 0;
                    estimate = new Estimate(task ,node) { conmonBeforeTime = conmonBeforeTime ,currentTime = node.R };
                    estimate.addBefore(before);
                }
                if(i!= list.Count - 1 ) {
                    estimate.conmonNextTime = getEdgeTime(node ,list[i + 1]);
                }
       
                before = estimate;
              
            }
            return before;
        }
   

       
        public static bool isArived(int begin, int end, String className)
        {
            lock (lockObj)
            {
                clearTmp();
                Node beginNode = getConfig(begin, true);
                Node endNode = getConfig(end, true);
                if (endNode == beginNode)
                {//移库或者移缓存
                    return true;
                }
                beginNode.tmpBeforDistance = beginNode.getPredictTime();
                int type = beginNode.dbStns[0].type;
                findStn(beginNode, className,type);
                return endNode.tmpBefore != null;
            }
        }
        public static List<TaskDto> getTaskPath(int begin, int end, String className)
        {
            lock (lockObj)
            {
                clearTmp();
                Node beginNode = getConfig(begin, true);
                Node endNode = getConfig(end, true);
                if (endNode == beginNode)
                {//移库或者移缓存
                    TaskDto edge = new TaskDto(endNode);
                    edge.src = begin;
                    edge.addNode(beginNode);
                    foreach (Edge tmp in allEdge)
                    {
                        if (tmp.getSrc() == endNode && tmp.getDes() == endNode)
                        {
                            edge.driveType = tmp.driveType;
                            return new List<TaskDto>() { edge };
                        }
                    }
                }
                beginNode.tmpBeforDistance = beginNode.getPredictTime();
                int type = beginNode.dbStns[0].type;
                findStn(beginNode, className,type);
                if (endNode.tmpBefore == null)
                {
                    return null;
                }
                return getbefore(endNode, begin);
            }
        }

        public static List<int> ariveOrder(int begin, int m1, int m2, int end)
        {
            lock (lockObj)
            {
                List<Node> allDict = MyCache.getList<Node>();
                foreach (var v in allDict)
                {
                    v.tmpBefore = null;
                    v.tmpBeforDistance = int.MaxValue;
                }
                Node beginConfig = getConfig(begin);
                Node endConfig = getConfig(end);
                beginConfig.tmpBeforDistance = 0;
                int type = beginConfig.dbStns[0].type;
                findStn(beginConfig, null,type);
                if (endConfig.tmpBefore == null)
                {
                    throw new Exception("path not arrived" + begin + "-》" + end);
                }
                return getbefore2(endConfig, m1, m2, begin);
            }
        }
        private static List<int> getbefore2(Node me, int m1, int m2, int begin)
        {
            List<int> edgeList = new List<int>();
            do
            {
                me = me.tmpBefore;
                if (me.isConfig(m1))
                {
                    edgeList.Add(m1);
                }
                if (me.isConfig(m2))
                {
                    edgeList.Add(m2);
                }
            } while (!me.isConfig(begin));
            edgeList.Reverse();
            return edgeList;
        }
        private static List<TaskDto> getbefore(Node me, int begin)
        {
            List<TaskDto> edgeList = new List<TaskDto>();
            TaskDto edge = new TaskDto(me);
            do
            {
                Node end = me;
                me = me.tmpBefore;
                edge.addNode(me);
                var e = getDriveType(me ,end);
                Type newType = e.driveType;
                if (edge.driveType == null)
                {
                    edge.driveType = newType;
                    edge.driveId = e.driveId;
                }
                else if (edge.driveType != newType)
                {
                    edge.src = end.eid;
                    edgeList.Add(edge);
                    edge = new TaskDto(end);
                    edge.addNode(me);
                    edge.driveType = newType;
                }
                if (me.isKeyStn)
                {
                    edge.src = me.eid;
                    edgeList.Add(edge);
                    edge = new TaskDto(me);
                }
                else if (me.isConfig(begin))
                {
                    edge.src = me.eid;
                    edgeList.Add(edge);
                }
            } while (!me.isConfig(begin));
            edgeList.Reverse();
            foreach (TaskDto dto in edgeList)
            {
                dto.src = Math.Abs(dto.src);
                dto.des = Math.Abs(dto.des);
            }
            return edgeList;
        }
        void setType()
        {

        }
        static Edge getDriveType(Node me, Node des)
        {
            foreach (Edge tmp in allEdge)
            {
                if (tmp.getSrc() == me && tmp.getDes() == des)
                {
                    return tmp;
                }
            }
            return null;
        }
        static public int getEdgeTime(Node me, Node des)
        {
            foreach (Edge tmp in allEdge)
            {
                if (tmp.getSrc() == me && tmp.getDes() == des)
                {
                    return tmp.R;
                }
            }
            return 10000000;
        }

        private static void findStn(Node begin, String className ,int type)
        {//递归找最短路径
            List<Edge> nexts = begin.nextEdge;
            foreach (Edge edge in nexts)
            {
                if (!String.IsNullOrWhiteSpace(className) && edge.driveType.Name != className)
                {
                    continue;
                }
                Node des = edge.getDes();
                if( edge.driveType.Name == "TaskCarrier" && des.dbStns[0].type != type ) {
                    continue;
                }


                int dis = begin.tmpBeforDistance + edge.R + des.getPredictTime();
                if ( des.tmpBeforDistance > dis)
                {
                    des.tmpBefore = begin;
                    des.tmpBeforDistance = dis;
                    findStn(des , className,type);
                }
                else
                {
                    //环线处理
                }
            }
        }

        public static object myOrderLock = new object();


        public static bool splitParam (CoreTaskParam p, CoreTask tc) {
            clearParamSplitData(p);
            if (p.status <= (int)CoreTaskParamStatus.删除 || p.cDes == p.des && p.status == (int)CoreTaskParamStatus.执行结束 || p.status == (int)CoreTaskParamStatus.执行完成) {
                return false;
            }
            LoggerCommon.fileAll("分解任务" + ObjStrUtil.mySerializeObject(p));
            lock (myOrderLock) {
                if (p.type == (int)TaskParamType.机器人分解) {
                    createTask(p, tc, TransferConfigFacade.robotType, null, CoreTaskParamStatus.待执行);
                    return true;
                } else {
                    if (!LocConfig.isLoc(p.src) && LocConfig.isLoc(p.des) && LocConfig.isLoc(p.cStn)) {
                        return false;

                    }
                    List<TaskDto> keyList = TransferConfigFacade.getTaskDtoList(p);
                    lock (obj) {
                        BaseTask before = null;
                        Estimate beforeEstimate = null;
                        for (int i = 0; i < keyList.Count; i++) {
                            TaskDto edge = keyList[ i ];
                            BaseTask tasks = createTask(p, tc, edge.driveType, edge, edge.initStatus);

                            beforeEstimate = TransferConfigFacade.setTaskDto(tasks, edge.list, beforeEstimate);
                            tasks.relyTask.Add(before.myOrder);
                            before = tasks;
                        }
                    }
                    return true;
                }
            }
        }


        static BaseTask createTask (CoreTaskParam p, CoreTask tc, Type t, TaskDto dto, CoreTaskParamStatus initStatus) {
            BaseTask task = (BaseTask)Activator.CreateInstance(t);
            task.taskNo = p.taskNo;


            task.init(tc, p, dto);

            task.status = (short)initStatus;
            List<BaseTask> relyTask = getTaskByParamId((p.relyParamId));
            foreach (BaseTask bt in relyTask) {
                task.relyTask.Add(bt.myOrder);
            }

            return task;
        }





        public static void removeParam (long id) {
            CoreTaskParam p = MyCache.getValue<CoreTaskParam>(id + "");
            clearParamSplitData(p);
            MyCache.remove<CoreTaskParam>(id + "");
        }

        public static void clearParamSplitData (CoreTaskParam p) {

            List<Estimate> estList = MyCache.query<Estimate>(a => {
                return a.taskNo == p.taskNo;
            });
            foreach (Estimate e in estList) {
                TransferConfigFacade.remove(e);
                MyCache.remove<Estimate>(e.id + "");
            }
            removeByParam(p.id);
        }

        public static void removeComplete (long id) {
            List<CoreTaskParam> paramList = MyCache.query<CoreTaskParam>((a) => {
                return a.taskId == id;
            });
            foreach (var p in paramList) {
                removeParam(p.id);
            }
        }

        public static bool test = false;

        public static BaseTask getlastTask (BaseTask task) {

            if (task == null) {
                return null;
            }
            List<CacheData> list = MyCache.getList(task.GetType());
            for (int i = 0; i < list.Count; i++) {
                BaseTask bt = (BaseTask)list[ i ];
                if (bt.taskNo == task.taskNo && bt.src == task.src && bt.des == task.des) {
                    return bt;

                }
            }

            return null;
        }





        public static void removeByParam (long paramId) {

            List<Type> list = TransferConfigFacade.getAllTaskType();
            List<BaseTask> res = new List<BaseTask>();
            foreach (Type t in list) {
                List<CacheData> li = MyCache.getList(t);
                IEnumerable<CacheData> res1 = li.Where((a) => {
                    BaseTask carrier = (BaseTask)a;

                    return carrier.completeParamId == paramId;
                });
                foreach (var k in res1) {
                    li.Remove((BaseTask)k);
                }

            }



        }



        public static List<BaseTask> getList (Type handle) {
            List<CacheData> list = MyCache.getList(handle);
            List<BaseTask> res = new List<BaseTask>();
            foreach (CacheData d in list) {

                res.Add((BaseTask)d);
            }
            return res;
        }

        public static List<T> query<T> (Func<BaseTask, bool> func) where T : BaseTask {
            Type type = typeof(T);

            IEnumerable<BaseTask> res = query(type, func);
            List<T> list = new List<T>();
            foreach (BaseTask bt in res) {

                list.Add((T)bt);
            }
            return list;
        }


        public static List<BaseTask> queryAllTask (Func<CacheData, bool> func) {
            List<Type> list = TransferConfigFacade.getAllTaskType();
            List<BaseTask> res = new List<BaseTask>();
            foreach (Type t in list) {
                List<BaseTask> li = MyCache.getList<BaseTask>(t);
                IEnumerable<BaseTask> res1 = li.Where(a => func(a));
                foreach (var k in res1) {
                    res.Add(k);
                }

            }



            return res.ToList();
        }

        public static List<BaseTask> query (Type type, Func<BaseTask, bool> func) {

            List<BaseTask> baselist = MyCache.getList<BaseTask>(type); ;
            IEnumerable<BaseTask> res = baselist.Where(a => func(a));

            return res.ToList();
        }

        static public List<T> queryTask<T> (Func<BaseTask, bool> func) where T : BaseTask {

            IEnumerable<T> res = query<T>(a => func(a));
            return res.ToList();
        }


        static public List<BaseTask> queryTask (Type type, Func<BaseTask, bool> func) {

            IEnumerable<BaseTask> res = query(type, a => func(a));
            return res.ToList();
        }

        public static BaseTask getTaskById (long id) {

            List<Type> list = TransferConfigFacade.getAllTaskType();
            foreach (Type t in list) {
                List<CacheData> li = MyCache.getList(t);
                foreach (var ta in li) {
                    BaseTask t1 = (BaseTask)ta;
                    if (t1.myOrder == id) {
                        return t1;
                    }
                }
            }

            return null;
        }
        public static object obj = new object();

        static public List<BaseTask> getTaskByParamId (Type type, long id) {
            lock (obj) {
                return queryTask(type, (a) => {
                    return a.completeParamId == id;
                });
            }
        }


        public static List<BaseTask> getTaskByParamId (long paramId) {
            List<BaseTask> res = new List<BaseTask>();
            if (paramId == 0) {
                return res;
            }

            List<Type> list = TransferConfigFacade.getAllTaskType();
            foreach (Type t in list) {
                List<CacheData> li = MyCache.getList(t);
                foreach (var ta in li) {
                    BaseTask t1 = (BaseTask)ta;
                    if (t1.completeParamId == paramId) {
                        res.Add(t1);
                    }
                }
            }


            return res;
        }
        static public List<BaseTask> getTaskByCompleteId (Type type, long id) {
            lock (obj) {
                List<BaseTask> list = queryTask(type, (a) => {
                    return a.completeId == id;
                });
                return list;
            }
        }
        static public List<T> getTaskListByTaskNo<T> (int taskNO) where T : BaseTask {
            Type type = typeof(T);
            lock (obj) {
                return queryTask<T>((a) => {
                    return a.taskNo == taskNO;
                });
            }
        }


        static public List<BaseTask> getTaskByCode (String code) {
            lock (obj) {

                IEnumerable<BaseTask> res = queryAllTask((a) => {
                    return ((BaseTask)a).code == code;
                });
                return res.ToList();
            }
        }


        static public N3 getNextTaskByTaskNo<N1, N2, N3> (int taskNo) where N1 : BaseTask where N2 : BaseTask where N3 : BaseTask {
            lock (obj) {
                N1 n1 = queryUnique<N1>((a) => {
                    return a.taskNo == taskNo;
                }, "任务" + taskNo);
                if (n1 == null) {
                    return null;
                }
                N2 n2 = queryUnique<N2>((a) => {
                    return a.completeId == n1.completeId && a.src == n1.des;
                }, n1.completeId + "-" + n1.des);
                if (n2 == null) {
                    return null;
                }
                N3 n3 = queryUnique<N3>((a) => {
                    return a.completeId == n1.completeId && a.src == n2.des;
                }, n1.completeId + "--" + n2.des);
                return (N3)n3;
            }
        }
        public List<T> getWorkingByDes<T> (int des, string code = null) where T : BaseTask {
            lock (obj) {
                return queryTask<T>((a) => {
                    T t = (T)a;
                    if (t.status > 9 || t.status < 1) {
                        return false;
                    }
                    return (String.IsNullOrWhiteSpace(code) || t.code == code) && (des == 0 || t.des == des);
                });
            }
        }
        public List<T> getCarrierTaskBetween<T> (int srcStart, int srcEnd, int desStart, int desEnd) where T : BaseTask {
            lock (obj) {
                return queryTask<T>((a) => {
                    T t = (T)a;
                    if (t.status >= 9 || t.status < 0) {
                        return false;
                    }
                    return t.src >= srcStart && t.src <= srcEnd || t.des >= desStart && t.des <= desEnd;
                });
            }
        }
        static public N1 getBefore<N1, N2> (int taskNo) where N1 : BaseTask where N2 : BaseTask {
            lock (obj) {
                N2 n2 = queryUnique<N2>((a) => {
                    return a.taskNo == taskNo;
                }, "任务" + taskNo);
                if (n2 == null) {
                    return null;
                }
                List<N1> n2List = queryTask<N1>((a) => {
                    return a.completeId == n2.completeId;
                });
                return n2List.Where(a => a.des == n2.src).FirstOrDefault();
            }
        }
        static public N2 getStart2ContinueTaskByStn<N1, N2> (int stn) where N1 : BaseTask where N2 : BaseTask {
            lock (obj) {
                List<N1> res1 = queryTask<N1>((a) => {
                    return a.des == stn;
                }).ToList();
                List<N2> res2 = queryTask<N2>((a) => {
                    return a.src == stn && a.status == (int)CoreTaskParamStatus.待执行;
                });
                for (int i = 0; i < res2.Count; i++) {
                    N2 n2 = res2[ i ];
                    foreach (N1 n1 in res1) {
                        if (n2.completeId == n1.completeId) {
                            if (n1.status != (int)CoreTaskParamStatus.执行结束) {
                                res2.RemoveAt(i);
                                i--;
                            }
                        }
                    }
                }


                int count = res2.Count();
                if (count > 1) {
                    throw new Exception(stn + "待执行任务过多");
                } else if (count == 1) {
                    return res2[ 0 ];
                } else {
                    return null;
                }
            }
        }


        static public N2 getStart2ContinueTaskByStn<N1, N2> (int stn, int taskNO) where N1 : BaseTask where N2 : BaseTask {
            lock (obj) {
                List<N1> res1 = queryTask<N1>((a) => {
                    return a.des == stn;
                }).ToList();
                List<N2> res2 = queryTask<N2>((a) => {
                    return a.src == stn && a.status == (int)CoreTaskParamStatus.待执行;
                });
                for (int i = 0; i < res2.Count; i++) {
                    N2 n2 = res2[ i ];
                    foreach (N1 n1 in res1) {
                        if (n2.completeId == n1.completeId) {
                            if (n1.status != (int)CoreTaskParamStatus.执行结束) {
                                res2.RemoveAt(i);
                                i--;
                            }
                        }
                    }
                }


                int count = res2.Count();

                foreach (N2 n2 in res2) {

                    if (n2.taskNo == taskNO) {
                        return n2;
                    }

                }

                return null;

            }
        }
        static public T getByCode<T> (string code, int startPath) where T : BaseTask {
            lock (obj) {
                return queryUnique<T>((a) => {
                    T carrier = (T)a;
                    return (String.IsNullOrWhiteSpace(code) || code == carrier.code) && (startPath == 0 || startPath == carrier.src);
                }, code + "从" + startPath);
            }
        }

        static public List<T> getTaskListByBegin<T> (string code, int startPath) where T : BaseTask {
            lock (obj) {
                return queryTask<T>((a) => {
                    T carrier = (T)a;
                    return (String.IsNullOrWhiteSpace(code) || code == carrier.code) && (startPath == 0 || startPath == carrier.src);
                });
            }
        }

        static public List<T> getCTaskDtoByBegin<T> (string code, int startPath) where T : BaseTask {
            lock (obj) {
                List<T> res = query<T>((a) => {
                    T carrier = (T)a;
                    bool r = (String.IsNullOrWhiteSpace(code) || code == carrier.code) && (startPath == 0 || startPath == carrier.src);

                    return r && carrier.status == 1;
                });
                return res;
            }
        }
        static public N getNextTaskByTaskNo<M, N> (int stn, int taskNo) where M : BaseTask where N : BaseTask {
            lock (obj) {
                M n1 = queryUnique<M>((a) => {
                    return a.taskNo == taskNo && a.des == stn;
                }, "任务" + taskNo);
                if (n1 == null) {
                    N n3 = queryUnique<N>((a) => {
                        return a.src == stn && a.taskNo == taskNo;
                    }, taskNo + "站台异常");
                    return n3;
                }
                N n2 = queryUnique<N>((a) => {
                    return a.completeId == n1.completeId && a.src == n1.des;
                }, n1.completeId + "" + n1.des);
                return n2;
            }
        }
        static public List<N> getNextTasksByTaskNo<M, N> (List<int> taskNo) where M : BaseTask where N : BaseTask {
            lock (obj) {
                List<long> resM = queryTask<M>((a) => {
                    return taskNo.Contains(a.taskNo);
                }).Select(a => a.completeId).ToList();
                return queryTask<N>((a) => {
                    return resM.Contains(a.completeId);
                });
            }
        }
        static public T getTaskByTaskNo<T> (int taskNo) where T : BaseTask {
            lock (obj) {
                return queryUnique<T>((a) => {
                    return a.taskNo == taskNo;
                }, "任务" + taskNo);
            }
        }
        static public T getTaskByTaskNo<T> (short driveId, int taskNo) where T : BaseTask {
            lock (obj) {
                return queryUnique<T>((a) => {
                    return a.taskNo == taskNo && driveId == a.driveId;
                }, "任务" + taskNo);
            }
        }



        static private T queryUnique<T> (Func<BaseTask, bool> func, String errorInfo) where T : BaseTask {
            IEnumerable<T> res = query<T>(a => func(a));
            if (res.Count() > 1) {
                throw new Exception(errorInfo + "任务数量过多");
            } else {
                T r = res.FirstOrDefault();
                if (r == null) {
                    return null;
                } else {
                    return (T)r;
                }
            }
        }
    }
}
